home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
magazine
/
drdobbs
/
ddjcompr
/
compact
/
compact.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-11-05
|
41KB
|
2,378 lines
/******************************************************************
* ex:se ts=4 sw=4 ai:
*
* Data compression program.
*
* Author: Gene H. Olson
* Smartware Consulting
*
* This is FREE software in the PUBLIC DOMAIN.
*
* This program may be used only for research into data
* compression techniques. See WARNING in compact(1)
* manual page for further discussion.
*
* "Everything FREE contains no guarantee."
********************************************************************/
#define VERSION "compact version 1.0 PL0"
#ifndef lint
static char sccs[] = "@(#)compact.c 1.25 10/28/90" ;
#endif
#include <sys/lock.h>
#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <signal.h>
#if MARK
# include <prof.h>
#else
# define MARK(x)
#endif
extern void exit() ;
extern char *memset() ;
extern char *memcpy() ;
extern char *share_create() ;
extern char *share_open() ;
#ifndef DEBUG
# ifdef lint
# define DEBUG 99
# else
# define DEBUG 0
# endif
#endif
#ifndef CHECK
# if DEBUG >= 2
# define CHECK 100
# else
# define CHECK 0
# endif
#endif
/*
* Pet typedefs.
*/
#define SWAP(x) (((ushort)(x) >> 8) | ((ushort)(x) << 8))
#define uchar unsigned char
#define ushort unsigned short
#define ulong unsigned long
#if lint
#define malloc(x) 0
#define realloc(x,y) 0
#endif
/*
* Fundamental constants.
*/
#define MAGIC 0xc5a3 /* Magic number */
#define ODDMAGIC 0xc3a4 /* EOF magic for odd files */
#define EVENMAGIC 0xc3a5 /* EOF magic for even files */
#define MAXCHAR ((1 << 16) + 1) /* Size of character set */
/*
* Tuning parameter defaults.
*/
#define MEMORY 0x40000000 /* Default bytes memory */
#define INRUN 10000000 /* Default bytes input run */
#ifndef HASHRATIO
#define HASHRATIO 1.2 /* Ratio of hash entries to items */
#endif
/*
* Compression item table definition.
*/
#define HASHFUN(prefix, suffix) \
((((((prefix) << 3) + (suffix)) << 4) - (prefix) + (suffix)) & (hashmask))
typedef struct h_struct ITEM ;
struct h_struct
{
ITEM* h_next ; /* Next item on hash list */
ITEM** h_last ; /* Link ptr to this item */
ulong h_code ; /* Previous code */
ushort h_char ; /* Current input character */
ushort h_ref ; /* Reference flag */
} ;
int hashsize ; /* Hash table size (power 2) */
int maxitem ; /* Maximum item number */
/*
* Decompression item table definition.
*/
typedef struct d_struct DEC ;
struct d_struct
{
ulong d_code ; /* Decompression code */
ushort d_char ; /* Decompression character */
ushort d_ref ; /* Reference flag */
} ;
/*
* Caught signal list.
*/
int sig[] = { SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGTERM, 0 } ;
/*
* Debugging stuff.
*/
#if DEBUG >= 1
int debug ; /* Debug level */
#endif
/*
* Efficiency checks.
*/
#if DEBUG >= 1
long collision ;
#endif
/*
* User parameters.
*/
int action ; /* c==compact or e==expand */
char *infname ; /* Input file name */
char *outfname ; /* Output file name */
int infd ; /* Input file descriptor */
int outfd ; /* Output file descriptor */
long inrun ; /* Maximum input run length */
long memory ; /* Memory to use */
long maxwidth ; /* Max width of output code */
int quiet ; /* Quiet mode */
int inbs ; /* Input block size */
int outbs ; /* Output block size */
int phys ; /* Output physical record size */
int insize ; /* Input volume size (records) */
int outsize ; /* Output volume size (records) */
ulong inrec ; /* Input record number */
ulong outrec ; /* Output record number */
int incopy ; /* Read input to non-shared buffer */
int outcopy ; /* Write output from non-shared buffer */
int inswap ; /* Swap input bytes */
int outswap ; /* Swap output bytes */
int memlock ; /* Lock process into memory */
int intank ; /* Number of input buffers */
int outtank ; /* Number of output buffers */
int inpid ; /* Input tank process PID */
int outpid ; /* Output tank process PID */
int (*getin)() ; /* Get input routine */
int (*putout)() ; /* Put output routine */
char *acommand ; /* Medium setup command */
char *zcommand ; /* Medium teardown command */
char *rsh ; /* Read share structure (opaque) */
char *wsh ; /* Write share structure (opaque) */
/*
* Miscellaneous declarations.
*/
int invol ; /* Input volume number */
int outvol ; /* Output volume number */
ushort *inbuf ; /* Input buffer */
ushort *outbuf ; /* Output buffer */
ushort *lastbuf ; /* Last buffer read by inprocess */
int lastcount ; /* Corresponding buffer count */
ulong intotal ; /* Input character count */
ulong outtotal ; /* Output character count */
ushort eofmagic ; /* EOF magic number */
/**************************************************************
* quit - Exit gracefully.
**************************************************************/
void
quit(s)
int s ;
{
register int i ;
register int pid ;
int rs ;
for (i = 0 ; sig[i] ; i++) (void) signal(sig[i], SIG_IGN) ;
/*
* Close shared memory.
*/
if (rsh) share_close(rsh) ;
if (wsh) share_close(wsh) ;
/*
* Wait for inprocess and outprocess to complete.
*/
while (inpid | outpid)
{
pid = wait(&rs) ;
if (pid == -1) break ;
if (inpid == pid)
{
inpid = 0 ;
if (s == 0) s = rs ;
}
if (outpid == pid)
{
outpid = 0 ;
if (s > 0) s = rs ;
}
}
/*
* Destroy allocated shared memory.
*/
if (rsh) share_destroy(rsh) ;
if (wsh) share_destroy(wsh) ;
/*
* Execute final teardown command.
*
* Note that if we were in compress mode with
* multi-volume input, the zcommand has already
* been done by the input() routine when prompting
* for the last volume of the set.
*/
if (s == 0 && zcommand && !(action == 'c' && insize))
(void) system(zcommand) ;
exit(s) ;
}
/***********************************************************
* getval - Get value
***********************************************************/
long
getval(cp, units)
register char *cp ;
int units ;
{
register int val ;
units = 0 ;
if (!isdigit(*cp)) return(-1) ;
val = 0 ;
do
{
val *= 10 ;
val += *cp++ - '0' ;
} while (isdigit(*cp)) ;
if (*cp) units = *cp++ ;
switch (units)
{
case 'B':
case 'b':
val *= 512 ;
break ;
case 'K':
case 'k':
val *= 1024 ;
break ;
case 'M':
case 'm':
val *= 1000 * 1024 ;
break ;
case 0:
break ;
default:
return(-1) ;
}
return (*cp==0 ? val : -1) ;
}
/************************************************************
* confirm - Wait for volume load confirmation.
************************************************************/
confirm()
{
register int n ;
char ch ;
int answer ;
static int ttyfd = -1 ;
/*
* Get a file descriptor to reference the
* users tty terminal.
*/
if (ttyfd < 0)
{
if (isatty(0)) ttyfd = 0 ;
else if ((ttyfd = open("/dev/tty", O_RDWR)) == -1)
{
(void) fprintf(stderr, "Cannot open ") ;
perror("/dev/tty") ;
quit(1) ;
}
}
/*
* Read the tty for the user's answer.
*/
answer = 0 ;
for (;;)
{
n = read(ttyfd, &ch, 1) ;
if (n == 1)
{
if (ch == '\r' || ch == '\n') break ;
if (answer == 0) answer = ch ;
}
if (n < 0)
{
perror("TTY") ;
quit(1) ;
}
}
return(answer) ;
}
/************************************************************
* input - Get an input block.
************************************************************/
input()
{
register int n ;
register int i ;
register ushort *wp ;
int ch ;
static int eoi ;
if (eoi) return(0) ;
/*
* Loop to read in a full input record.
*/
for (n = 0 ;;)
{
/*
* Handle multi-volume input.
*/
if (infname && inrec == 0)
{
/*
* If not the first volume of an n-volume set,
* ask for confirmation before opening the file.
*
* If he responds with (q), assume the last
* volume has been read, and return end-of-file.
*/
if (++invol > 1)
{
if (zcommand) (void) system(zcommand) ;
for (;;)
{
(void) fprintf(stderr,
"Mount input volume #%d, hit <RETURN> (or 'q' to quit)? ",
invol) ;
ch = confirm() ;
if (ch == 0) break ;
if (ch == 'q')
{
eoi = 1 ;
goto done ;
}
}
if (acommand) (void) system(acommand) ;
}
/*
* Open the file.
*/
if ((infd = open(infname, O_RDONLY)) == -1)
{
(void) fprintf(stderr, "Cannot open ") ;
perror(infname) ;
quit(1) ;
}
}
/*
* Read in records until we get a full record, an
* end-of-file, or an error.
*/
for (;;)
{
i = read(infd, (char*)inbuf + n, inbs - n) ;
/*
* Handle EOF. If we are not doing multi-volume input
* consider this the EOI. If we are doing multi-volume
* input, go back for the next volume.
*/
if (i == 0)
{
(void) close(infd) ;
if (insize)
{
inrec = 0 ;
break ;
}
eoi = 1 ;
goto done ;
}
/*
* Handle a file I/O error.
*/
if (i == -1)
{
n = -1 ;
perror("Input read error") ;
(void) close(infd) ;
return(-1) ;
}
/*
* A good read. Add the bytes read this time to
* the running total. When we get a full record,
* increment the volume record count. When the
* full volume size is reached, reset the record
* position so we go back for another volume next
* time.
*/
n += i ;
if (n == inbs)
{
if (++inrec == insize)
{
(void) close(infd) ;
inrec = 0 ;
}
goto done ;
}
}
}
/*
* If byte swapping is in effect, swap all the
* bytes in the current input buffer.
*/
done:
if (inswap)
{
wp = inbuf ;
i = (n + 1) / 2 ;
while (--i >= 0)
{
*wp = SWAP(*wp) ;
wp++ ;
}
}
return(n) ;
}
/******************************************************************
* fileinput - File input.
******************************************************************/
fileinput()
{
register int n ;
n = input() ;
if (n > 0)
{
intotal += n ;
if (n & 1) eofmagic = ODDMAGIC ;
}
return((n + 1) / 2 - 1) ;
}
/******************************************************************
* inprocess - Input process
*
* This procedure forks, then runs as a process reading from
* the input device or pipe.
******************************************************************/
inprocess()
{
register char *sh ;
int rc ;
ushort *buf ;
ushort *ibuf ;
ushort *wp ;
int pfd[2] ;
int cfd[2] ;
rsh = share_create(pfd, cfd, inbs, intank) ;
if (rsh == 0)
{
(void) fprintf(stderr, "Shared memory allocated failed\n") ;
quit(1) ;
}
/*
* Create the task. Setup file descriptors so the
* inprocess will die on SIGPIPE if the primary process
* exits, but no SIGPIPE occurs to the promary process
* when inprocess exits.
*/
while ((inpid = fork()) == -1) sleep(1) ;
if (inpid == 0)
{
(void) close(1) ;
(void) close(cfd[0]) ;
(void) close(cfd[1]) ;
/*
* If input is done to a non-shared buffer,
* allocate the buffer here.
*/
if (incopy)
{
buf = (ushort *)malloc(inbs) ;
if (buf == 0)
{
(void) fprintf(stderr, "Not enough memory\n") ;
exit(1) ;
}
}
else buf = 0 ;
/*
* Loop reading input blocks until done.
*/
sh = share_open(pfd) ;
assert(sh) ;
for (;;)
{
/*
* Get a block, read data into it, then pass it
* to the data compression process.
*/
if (share_get(sh, (char**)&inbuf, &rc) <= 0) break ;
if (buf)
{
ibuf = inbuf ;
inbuf = buf ;
}
if ((rc = input()) <= 0) break ;
if (buf)
{
inbuf = ibuf ;
(void) memcpy((char *)inbuf, (char *)buf, rc) ;
}
if (share_put(sh, (char*)inbuf, rc) <= 0) break ;
/*
* In data compression mode with multi-volume
* input, detect EOI here so as to avoid prompting
* the user unnecessarily for another volume.
*/
if (action == 'e' && insize)
{
wp = inbuf + rc / sizeof(ushort) ;
while (wp > inbuf && *--wp == 0) ;
if ( wp >= inbuf
&& (*wp == ODDMAGIC || *wp == EVENMAGIC)
&& (wp == inbuf || *--wp == 0)
&& (wp == inbuf || *--wp == 0)
&& (wp == inbuf || *--wp == 0)
)
break ;
}
}
#if DEBUG >= 2
if (debug >= 2)
{
(void) fprintf(stderr, "Input child terminating normally\n") ;
}
#endif
/*
* Exit the child when complete.
*/
share_close(sh) ;
exit(0) ;
}
/*
* Initialize the shared memory interface in the
* parent process.
*/
(void) close(pfd[1]) ;
rsh = share_open(cfd) ;
}
/************************************************************
* shareinput - Do input from shared memory.
************************************************************/
shareinput()
{
int n ;
if (inbuf) (void) share_put(rsh, (char*)inbuf, 0) ;
if (share_get(rsh, (char**)&inbuf, &n) <= 0) return(-1) ;
if (n > 0)
{
intotal += n ;
if (n & 1) eofmagic = ODDMAGIC ;
}
return((n + 1) / 2 - 1) ;
}
/***********************************************************
* output - Output a block of data.
***********************************************************/
output(nbyte)
int nbyte ; /* Number of bytes to write */
{
register ushort *wp ;
register int n ;
register int bsize ;
/*
* Open the output file if necessary.
*/
if (outfname && outrec == 0)
{
if (++outvol > 1)
{
if (zcommand) (void) system(zcommand) ;
(void) fprintf(stderr,
"Please mount output volume #%d, hit <RETURN> ",
outvol) ;
(void) confirm() ;
if (acommand) (void) system(acommand) ;
}
if ((outfd = open(outfname, O_WRONLY)) == -1)
{
(void) fprintf(stderr, "Cannot open ") ;
perror(outfname) ;
quit(1) ;
}
}
/*
* Swap output bytes if that option is selected.
*/
if (outswap)
{
n = nbyte / 2 + 1 ;
wp = outbuf ;
while (--n >= 0)
{
*wp = SWAP(*wp) ;
wp++ ;
}
}
/*
* Pad to a multiple of the given physical size if
* that option is selected.
*/
if (nbyte < outbs && phys)
{
wp = &outbuf[nbyte/2] ;
bsize = (nbyte + phys - 1) / phys * phys ;
while (nbyte < bsize)
{
*wp++ = 0 ;
nbyte += 2 ;
}
}
/*
* Write the data and check for errors.
*/
n = write(outfd, (char*) outbuf, (int) nbyte) ;
if (n < 0)
{
perror("Write error") ;
return(0) ;
}
if (n != nbyte)
{
(void) fprintf(stderr,
"Write of %d bytes returned %d\n", nbyte, n) ;
}
if (++outrec == outsize)
{
outrec = 0 ;
(void) close(outfd) ;
}
return(outbs) ;
}
/*****************************************************************
* fileoutput - File oriented output routine.
*****************************************************************/
fileoutput(n)
{
if (n < outbs && phys) outtotal += (n + phys - 1) / phys * phys ;
else outtotal += n ;
return(output(n) / 2) ;
}
/******************************************************************
* outprocess - Output process
*
* This procedure forks, then runs as a process writing to
* the output device or pipe.
******************************************************************/
outprocess()
{
int pfd[2] ;
int cfd[2] ;
int wcount ;
int n ;
char *sh ;
ushort *obuf ;
ushort *buf ;
wsh = share_create(pfd, cfd, outbs, outtank) ;
if (wsh == 0)
{
(void) fprintf(stderr, "Shared memory allocated failed\n") ;
quit(1) ;
}
/*
* Create the task. Setup file descriptors so the
* primary process will die on SIGPIPE if outprocess
* exits, but no SIGPIPE occurs to outprocess when
* the primary process exits.
*/
while ((outpid = fork()) == -1) sleep(1) ;
if (outpid == 0)
{
(void) close(0) ;
(void) close(pfd[1]) ;
sh = share_open(cfd) ;
assert(sh) ;
if (outcopy)
{
buf = (ushort *)malloc(outbs) ;
if (buf == 0)
{
(void) fprintf(stderr, "Not enough memory!\n") ;
exit(1) ;
}
}
else buf = 0 ;
for (;;)
{
if (share_get(sh, (char**)&outbuf, &wcount) <= 0) break ;
if (buf)
{
(void) memcpy((char *)buf, (char *)outbuf, wcount) ;
obuf = outbuf ;
outbuf = buf ;
}
if (output(wcount) <= 0) break ;
if (buf) outbuf = obuf ;
if (share_put(sh, (char*) outbuf, 0) <= 0) break ;
}
share_close(sh) ;
exit(0) ;
}
(void) close(cfd[0]) ;
(void) close(cfd[1]) ;
wsh = share_open(pfd) ;
(void) share_get(wsh, (char**)&outbuf, &n) ;
}
/********************************************************
* shareoutput - Shared memory output routine.
********************************************************/
shareoutput(n)
int n ;
{
if (n < outbs && phys) outtotal += (n + phys - 1) / phys * phys ;
else outtotal += n ;
if (share_put(wsh, (char*) outbuf, n) <= 0) return(0) ;
if (share_get(wsh, (char**)&outbuf, &n) <= 0) return(0) ;
return(outbs/2) ;
}
/*************************************************************
* check - keep track of the progress of the algorithm.
*************************************************************/
#if CHECK
check(incount, outptr)
int incount ;
ushort *outptr ;
{
ulong in ;
ulong out ;
float avg ;
float ratio ;
static ulong lastin ;
static ulong lastout ;
in = intotal - incount * sizeof(ushort) ;
out = outtotal + (long) outptr - (long) outbuf ;
avg = 100.0 * (float) out / (float) in ;
ratio = 100.0 * (float) (out - lastout) / (float) (in - lastin) ;
lastin = in ;
lastout = out ;
#if DEBUG >= 1
if (debug >= 1) (void) fprintf(stderr,
"Read: %-8ld Wrote: %-8ld Col: %-8ld Ratio: %6.2f%% %6.2f%%\n",
in, out, collision, ratio, avg
) ;
#endif
}
#endif
/**************************************************************
* compact - Compression algorithm.
**************************************************************/
compact()
{
register ITEM *hp ;
register ITEM *sp ;
register ITEM **hpp ;
register ushort *inptr ;
register ushort *outptr ;
register ulong ch ;
register ulong code ;
register ulong out ;
register int obit ;
register int osize ;
register int incount ;
register int outcount ;
register ulong nitem ;
register ulong mitem ;
register ulong hashmask ;
ITEM *itemtable ;
ITEM **hashtable ;
ITEM *ep ;
long bitcount ;
long blockcount ;
long i ;
ushort header[10] ;
#if CHECK
long chkcount = CHECK ;
#endif
/*
* If max output width is given, set maxitem accordingly.
*/
maxitem = (1 << maxwidth) - MAXCHAR ;
/*
* Reduce maxitem if necessary to fit into the requested
* maximum memory.
*/
i = memory / (sizeof(ITEM) + sizeof(ITEM *)) ;
if (maxitem > i) maxitem = i ;
/*
* Set the hashtable to have the same approximate number
* of entries as in maxitem, rounded to the closest
* power of 2.
*/
hashsize = HASHRATIO * maxitem ;
while (i = hashsize & (hashsize - 1)) hashsize = i ;
hashmask = hashsize - 1 ;
#if DEBUG >= 2
if (debug >= 2)
{
(void) fprintf(stderr,
"hashsize = %ld (%ldK), maxitem = %ld (%ldK)\n",
hashsize, sizeof(ITEM *) * hashsize / 1024,
maxitem, sizeof(ITEM) * maxitem / 1024) ;
}
#endif
/*
* Allocate hash table and item table.
*/
itemtable = (ITEM *)malloc(maxitem * sizeof(ITEM)) ;
hashtable = (ITEM **)malloc(hashsize * sizeof(ITEM *)) ;
if (itemtable == 0 || hashtable == 0)
{
(void) fprintf(stderr,
"Cannot allocate enough memory!\n") ;
exit(2) ;
}
/*
* Initialize output.
*/
outcount = outbs / 2 + 1 ;
outptr = outbuf ;
eofmagic = EVENMAGIC ;
/*
* Each pass through the loop, compress one block
* of data.
*/
for (;;)
{
#if DEBUG >= 1
if (debug >= 1) (void) fprintf(stderr, "\n") ;
#endif
/*
* Initialize for a new scan.
*/
(void) memset((char*)hashtable, 0, (int)(hashsize * sizeof(ITEM *))) ;
nitem = 0 ;
sp = &itemtable[0] ;
ep = &itemtable[maxitem] ;
out = 0 ;
obit = 0 ;
code = 0 ;
osize = 17 ;
bitcount = (1 << 16) ;
blockcount = inrun / inbs + .5 ;
/*
* Prepare the Magic number, and the maximum number
* of output codes for transmission.
*/
mitem = maxitem ;
header[0] = MAGIC ;
header[1] = 17 ;
header[2] = maxitem & 0xffff ;
header[3] = maxitem >> 16 ;
for (i = 0 ; i < 4 ; i++)
{
if (--outcount <= 0)
{
if ((outcount = (*putout)(outbs)) == 0) return(1) ;
outptr = outbuf ;
}
*outptr++ = header[i] ;
}
/*
* Read in the first input character of the run.
*/
if ((incount = (*getin)()) < 0) goto endrun ;
inptr = inbuf ;
ch = *inptr++ ;
/*
* Each pass through the loop, compress 1 or
* more input characters to output 1 compression
* code.
*/
for (;;)
{
MARK(cmatch) ;
code = ch + 1 ;
#if DEBUG >= 3
if (debug >= 3) (void) fprintf(stderr,
"\nMatching char: %04x\n", ch) ;
#endif
/*
* Each time through the loop, match the next
* input character if possible and continue.
* Exit when the match fails.
*/
for (;;)
{
/*
* Read in the next input character.
*/
if (--incount < 0)
{
if (--blockcount == 0)
{
incount = 0 ;
goto endrun ;
}
#if CHECK
if (--chkcount <= 0)
{
check(0, outptr) ;
chkcount = CHECK ;
}
#endif
if ((incount = (*getin)()) < 0) goto endrun ;
inptr = inbuf ;
}
ch = *inptr++ ;
#if DEBUG >= 3
if (debug >= 3) (void) fprintf(stderr,
"Read input char: %04x", ch) ;
#endif
/*
* Search the hash table for the required
* last code and current character.
*/
hpp = &hashtable[HASHFUN(code, ch)] ;
for (;;)
{
if ((hp = *hpp) == 0) goto miss ;
if (hp->h_code == code && hp->h_char == ch) break ;
hpp = &hp->h_next ;
#if DEBUG >= 1
collision++ ;
#endif
}
code = hp - itemtable + MAXCHAR ;
#if DEBUG >= 3
if (debug >= 3) (void) fprintf(stderr,
"\tMatched code: %lx\n", (ulong) code) ;
#endif
}
/*
* Output the next code.
*/
miss:
MARK(cout) ;
out |= code << obit ;
obit += osize ;
if (--outcount <= 0)
{
if ((outcount = (*putout)(outbs)) == 0) return(1) ;
outptr = outbuf ;
}
*outptr++ = out ;
out >>= 16 ;
obit -= 16 ;
if (obit >= 16)
{
if (--outcount <= 0)
{
if ((outcount = (*putout)(outbs)) == 0) return(1) ;
outptr = outbuf ;
}
*outptr++ = out ;
if (obit > 16) out |= code >> (osize - obit) ;
out >>= 16 ;
obit -= 16 ;
}
#if DEBUG >= 3
if (debug >= 3) (void) fprintf(stderr,
"\nOutput code: %lx\n", (ulong) code) ;
#endif
MARK(cbuild) ;
sp->h_next = 0 ;
sp->h_last = hpp ;
*hpp = sp ;
sp->h_code = code ;
sp->h_char = ch ;
sp->h_ref = 0 ;
sp++ ;
if (code >= MAXCHAR) itemtable[code - MAXCHAR].h_ref = 1 ;
#if DEBUG >= 3
if (debug >= 3)
{
(void) fprintf(stderr,
"Created compression code: %05lx ( %05lx %04x )\n",
sp - itemtable + MAXCHAR - 1, code, ch) ;
}
#endif
/*
* If we added a new entry to the table, expand
* the output bitsize if necessary.
*/
if (nitem < mitem)
{
MARK(calloc) ;
if (--bitcount == 0)
{
bitcount = 1 << osize ;
osize++ ;
#if DEBUG >= 2
if (debug >= 2) (void) fprintf(stderr,
"nitem = %d, osize = %d, bitcount = %d\n",
nitem, osize, bitcount) ;
#endif
}
if (++nitem != mitem) continue ;
}
/*
* When the compress item table has become full,
* find the least-recently-used leaf node and
* reallocate it.
*/
MARK(cscan) ;
for (;;)
{
if (sp == ep) sp = itemtable ;
if (sp->h_ref == 0) break ;
if (sp->h_code >= MAXCHAR)
{
itemtable[sp->h_code - MAXCHAR].h_ref = 1 ;
}
sp->h_ref = 0 ;
sp++ ;
}
*sp->h_last = sp->h_next ;
if (sp->h_next) sp->h_next->h_last = sp->h_last ;
}
/*
* Flush the output buffer, and add a few bytes
* of zero to guarantee synchronization.
*/
endrun:
MARK(cend) ;
#if DEBUG >= 3
if (debug >= 3) (void) fprintf(stderr,
"\nOutput final code: %lx\n", (ulong) code) ;
#endif
for (i = 0 ; i < 6 ; i++)
{
if (--outcount <= 0)
{
if ((outcount = (*putout)(outbs)) == 0) return(1) ;
outptr = outbuf ;
}
out |= code << obit ;
*outptr++ = out ;
out >>= 16 ;
code >>= 16 ;
}
if (incount < 0) break ;
#if CHECK
check(incount, outptr) ;
chkcount = CHECK ;
#endif
}
/*
* Add the EOF code at the end.
*/
if (--outcount <= 0)
{
if ((outcount = (*putout)(outbs)) == 0) return(1) ;
outptr = outbuf ;
}
*outptr++ = eofmagic ;
if ( outptr != outbuf
&& (*putout)((int) ((long)outptr-(long)outbuf)) == 0
)
return(1) ;
#if DEBUG >= 1
if (debug >= 1) check(0, outbuf) ;
#endif
if (!quiet && intotal != 0)
{
(void) fprintf(stderr,
"Read %ld, Wrote %ld, Compression %6.2f%%\n",
intotal, outtotal, 100.0 * (long)(intotal - outtotal) / intotal
) ;
}
#if DEBUG >= 1
if (!quiet && intotal > 1)
{
(void) fprintf(stderr,
"Collisions: %ld, %6.2f %%\n",
collision,
(100.0 * 17.0 / 16.0 * collision) / (intotal-1)
) ;
}
#endif
return(0) ;
}
/***********************************************************
* expand - Expand previously compressed data.
***********************************************************/
expand()
{
register DEC *sp ;
register DEC *dp ;
register ushort *bp ;
register ushort *outptr ;
register ushort *inptr ;
register int ibit ;
register ulong in ;
register ulong ch ;
register ulong code ;
register ulong lastcode ;
register int incount ;
register int outcount ;
int bitcount ;
ulong maxcode ;
DEC *dectable ;
DEC *ep ;
int isize ;
ulong imask ;
ushort *ip ;
ushort *buffer ;
int ic ;
int i ;
ulong skip ;
ulong zskip ;
int state ;
ulong ncode ;
int err ;
int magic ;
int nbyte ;
ushort header[4] ;
/*
* Initialize I/O buffers.
*/
err = 0 ;
incount = 0 ;
magic = 0 ;
outptr = outbuf ;
outcount = outbs / 2 + 1 ;
maxcode = 0 ;
dectable = 0 ;
buffer = 0 ;
/*
* Each time through the loop, process a complete
* block of data.
*/
state = 4 ;
for (;;)
{
/*
* Synchronize to the beginning of a new block of data.
*
* The file begins immediately with a magic number.
*
* Thereafter, blocks of decompression data are delimited
* by at least two bytes of zero, followed by the two
* byte magic number.
*/
MARK(esync) ;
skip = 0 ;
zskip = 0 ;
for (;;)
{
if (--incount < 0)
{
if ((incount = (*getin)()) < 0) break ;
inptr = inbuf ;
}
in = *inptr++ ;
if (in == 0)
{
if (state <= 4) state++ ;
else zskip += sizeof(ushort) ;
}
else
{
if (state >= 2)
{
if (in == MAGIC)
{
magic = 1 ;
break ;
}
if (in == (SWAP(MAGIC) & 0xffff))
{
if (!quiet) (void) fprintf(stderr,
"Automatically reversing byte order!\n") ;
inswap = !inswap ;
outswap = !outswap ;
ip = inptr ;
ic = incount ;
while (--ic >= 0)
{
*ip = SWAP(*ip) ;
ip++ ;
}
magic = 1 ;
break ;
}
if ((in == ODDMAGIC || in == EVENMAGIC) && magic)
{
eofmagic = in ;
magic = 2 ;
incount = -1 ;
break ;
}
}
skip += sizeof(ushort) ;
state = 0 ;
}
}
/*
* Mention that data was skipped.
*/
if (skip != 0 || zskip != 0 && incount >= 0)
{
(void) fprintf(stderr,
"Warning: %d bytes of unintelligible data skipped!\n",
skip + zskip) ;
}
if (incount < 0)
{
if (magic == 0) (void) fprintf(stderr,
"Warning: Magic number not found!\n") ;
if (magic == 1) (void) fprintf(stderr,
"Warning: Old style EOF encountered!\n") ;
goto eof ;
}
/*
* Get the maximum code present in the data.
*/
for (i = 1 ; i < 4 ; i++)
{
if (--incount < 0)
{
if ((incount = (*getin)()) < 0)
{
(void) fprintf(stderr, "EOF in header!\n") ;
err = 1 ;
goto eof ;
}
inptr = inbuf ;
}
header[i] = *inptr++ ;
}
code = header[3] ;
code <<= 16 ;
code |= header[2] ;
#if DEBUG >= 2
if (debug >= 2) (void) fprintf(stderr,
"Read header, nbits=%d, maxitem=%d\n", header[1], code) ;
#endif
/*
* Allocate the required memory.
*/
if (maxcode != code)
{
dectable = dectable
? (DEC *) realloc((char*) dectable, code * sizeof(DEC))
: (DEC *) malloc(code * sizeof(DEC))
;
buffer = buffer
? (ushort *) realloc((char*)buffer, code * sizeof(ushort))
: (ushort *) malloc(code * sizeof(ushort))
;
if (dectable == 0 || buffer == 0)
{
(void) fprintf(stderr,
"Cannot get enough memory to decode %d items\n", code) ;
exit(1) ;
}
maxcode = code ;
}
sp = &dectable[0] ;
ep = &dectable[maxcode] ;
/*
* Initialize to decompress this block of data.
*/
in = 0 ;
ibit = 0 ;
ncode = 0 ;
bp = buffer ;
isize = 17 ;
bitcount = (1 << 16) ;
imask = 0x1ffff ;
/*
* Read in the first item, which must be a
* single character.
*/
do
{
if (--incount < 0)
{
if ((incount = (*getin)()) < 0)
{
(void) fprintf(stderr, "Premature eof!\n") ;
err = 1 ;
goto eof ;
}
inptr = inbuf ;
}
in |= *inptr++ << ibit ;
ibit += 16 ;
} while (ibit < 17) ;
code = in & imask ;
if (code == 0)
{
state = 0 ;
continue ;
}
in >>= 17 ;
ibit -= 17 ;
if (code == 0)
{
(void) fprintf(stderr, "Null compression block!\n") ;
break ;
}
ch = code - 1 ;
#if DEBUG >= 3
if (debug >= 3) (void) fprintf(stderr,
"First input code: %05lx output as < %04x >\n", code, ch) ;
#endif
if (--outcount <= 0)
{
if ((outcount = (*putout)(outbs)) == 0) return(1) ;
outptr = outbuf ;
}
*outptr++ = ch ;
/*
* Each time through the loop, read and process one
* compressed item code.
*/
MARK(eloop) ;
for (;;)
{
lastcode = code ;
/*
* Expand the input bitsize if necessary.
*/
if (ncode < maxcode)
{
MARK(ealloc) ;
if (--bitcount == 0)
{
bitcount = 1 << isize ;
isize++ ;
imask += imask + 1 ;
#if DEBUG >= 2
if (debug >= 2) (void) fprintf(stderr,
"ncode = %d, isize = %d, bitcount = %d\n",
ncode, isize, bitcount) ;
#endif
}
ncode++ ;
}
/*
* If the table is full, find the oldest leaf node
* and reallocate it.
*/
else
{
MARK(escan) ;
for (;;)
{
if (sp == ep) sp = dectable ;
if (sp->d_ref == 0) break ;
if (sp->d_code >= MAXCHAR)
{
dectable[sp->d_code - MAXCHAR].d_ref = 1 ;
}
sp->d_ref = 0 ;
sp++ ;
}
}
/*
* Read in the next compression code.
*/
MARK(eread) ;
if (--incount < 0)
{
if ((incount = (*getin)()) < 0)
{
(void) fprintf(stderr, "Premature eof\n") ;
err = 1 ;
goto eof ;
}
inptr = inbuf ;
}
in |= *inptr++ << ibit ;
ibit += 16 ;
if (ibit >= isize)
{
code = in & imask ;
in >>= isize ;
ibit -= isize ;
}
else
{
if (--incount < 0)
{
if ((incount = (*getin)()) < 0)
{
(void) fprintf(stderr, "Premature eof\n") ;
err = 1 ;
goto eof ;
}
inptr = inbuf ;
}
if (ibit <= 16)
{
in |= (ulong) *inptr++ << ibit ;
code = in & imask ;
in >>= isize ;
ibit -= isize - 16 ;
}
else
{
in |= (ulong) *inptr << ibit ;
code = in & imask ;
in >>= isize ;
in |= (ulong) *inptr++ >> (isize - ibit) ;
ibit -= isize - 16 ;
}
}
#if DEBUG >= 4
if (debug >= 4) (void) fprintf(stderr,
"Read input code: %05lx\n", code) ;
#endif
/*
* If a zero code is detected, start a new
* decompression pass.
*
* If a character code is detected, output it.
*/
MARK(eexpand) ;
if (code < MAXCHAR)
{
if (code == 0) break ;
ch = code ;
}
/*
* Expand a compression code.
*
* If the code is the next one to be allocated,
* then it was allocated by the compression program
* already to be the lastcode + the first character
* of the last code.
*
* Also check for an out-of-range decompression
* code. Signal an error in this case.
*/
else
{
if (code - MAXCHAR >= ncode)
{
(void) fprintf(stderr,
"Decompress error - attempting resync\n") ;
break ;
}
dp = &dectable[code - MAXCHAR] ;
if (dp == sp)
{
if (sp - dectable == lastcode - MAXCHAR)
{
(void) fprintf(stderr,
"Nasty decompress error - attempting resync\n") ;
break ;
}
*bp++ = ch ;
ch = lastcode ;
if (ch >= MAXCHAR)
{
dp = &dectable[ch - MAXCHAR] ;
for (;;)
{
*bp++ = dp->d_char ;
ch = dp->d_code ;
if (ch < MAXCHAR) break ;
dp = &dectable[ch - MAXCHAR] ;
}
}
}
else
{
for (;;)
{
*bp++ = dp->d_char ;
ch = dp->d_code ;
if (ch < MAXCHAR) break ;
dp = &dectable[ch - MAXCHAR] ;
}
}
}
ch -= 1 ;
/*
* Output the compression code data.
*/
MARK(eout) ;
#if DEBUG >= 3
if (debug >= 3)
{
ushort *wp ;
(void) fprintf(stderr, "Expanded code %05lx to <", code) ;
wp = bp ;
*wp++ = ch ;
do
{
(void) fprintf(stderr, " %04x", *--wp) ;
} while (wp != buffer) ;
(void) fprintf(stderr, " >\n") ;
}
#endif
if (--outcount <= 0)
{
if ((outcount = (*putout)(outbs)) == 0) return(1) ;
outptr = outbuf ;
}
*outptr++ = ch ;
while (bp != buffer)
{
if (--outcount <= 0)
{
if ((outcount = (*putout)(outbs)) == 0) return(1) ;
outptr = outbuf ;
}
*outptr++ = *--bp ;
}
/*
* Create a new code from the input data.
*/
MARK(ebuild) ;
sp->d_code = lastcode ;
sp->d_char = ch ;
sp->d_ref = 0 ;
sp++ ;
if (lastcode >= MAXCHAR) dectable[lastcode - MAXCHAR].d_ref = 1 ;
#if DEBUG >= 3
if (debug >= 3)
{
(void) fprintf(stderr,
"Created compression code: %05lx ( %05lx %04x )\n",
sp - dectable + MAXCHAR - 1, lastcode, ch) ;
}
#endif
}
state = 0 ;
}
eof:
nbyte = (long)outptr - (long)outbuf ;
if (eofmagic == ODDMAGIC) nbyte-- ;
if (nbyte && (*putout)(nbyte) == 0) return(1) ;
if (!quiet && intotal != 0)
{
(void) fprintf(stderr,
"Read %ld, Wrote %ld, Expansion %6.2f%%\n",
intotal, outtotal,
100.0 * (long)(outtotal - intotal) / intotal) ;
}
return(err) ;
}
/**************************************************************
* main - main program.
**************************************************************/
main(argc, argv)
int argc ;
char **argv ;
{
register int i ;
register int c ;
register int err ;
register int rs ;
register char *p ;
extern char *optarg ;
extern int optind ;
/*
* Set default parameters.
*/
infd = 0 ;
outfd = 1 ;
inbs = BUFSIZ ;
outbs = BUFSIZ ;
memory = MEMORY ;
maxwidth = 17 ;
inrun = INRUN ;
/*
* If command begins with 'e' or 'u', expand the
* input file. Otherwise compress it.
*/
p = argv[0] ;
action = *p ;
while (*p) if (*p++ == '/') action = *p ;
if (action == 'u') action = 'e' ;
else if (action != 'e') action = 'c' ;
#if DEBUG >= 1
debug = DEBUG ;
#endif
/*
* Unpack parameters.
*/
err = 0 ;
for (;;)
{
c = getopt(argc, argv, "a:b:B:cd:ef:F:lm:n:N:p:qr:R:sSt:T:uvw:xXz:") ;
if (c == EOF) break ;
switch (c)
{
/*
* Media setup command to be performed before
* first open.
*/
case 'a':
acommand = optarg ;
break ;
/*
* Input/output block size.
*/
case 'b':
inbs = getval(optarg, 'k') ;
if (inbs <= 2 || (inbs & 1)) err++ ;
break ;
case 'B':
outbs = getval(optarg, 'k') ;
if (outbs <= 2 || (outbs & 1)) err++ ;
break ;
/*
* Compress or expand action.
*/
case 'c':
case 'e':
action = c ;
break ;
/*
* Debug level.
*/
#if DEBUG >= 1
case 'd':
debug = atoi(optarg) ;
if (debug > DEBUG) err++ ;
break ;
#endif
/*
* Input/Output file name.
*/
case 'f':
infname = optarg ;
break ;
case 'F':
outfname = optarg ;
break ;
/*
* Lock process into memory.
*/
case 'l':
memlock = 1 ;
break ;
/*
* Amount of memory to use.
*/
case 'm':
memory = getval(optarg, 'k') ;
if (memory < 1) err++ ;
break ;
/*
* Physical record size. Causes the last output
* record to be padded with zeros to the next
* multiple of this size if necessary.
*/
case 'p':
phys = getval(optarg, 'k') ;
if (phys <= 2 || (phys & 1)) err++ ;
break ;
/*
* Quiet mode.
*/
case 'q':
quiet = 1 ;
break ;
/*
* Number of input/output records in volume.
*/
case 'n':
insize = getval(optarg, 'k') ;
if (insize < 1) err++ ;
break ;
case 'N':
outsize = getval(optarg, 'k') ;
if (outsize < 1) err++ ;
break ;
/*
* Maximum run length.
*/
case 'r':
inrun = getval(optarg, 0) ;
if (inrun < 5) err++ ;
break ;
/*
* Swap input or output bytes.
*/
case 's':
inswap++ ;
break ;
case 'S':
outswap++ ;
break ;
/*
* Tank size.
*/
case 't':
intank = getval(optarg, 0) ;
if (intank < 2 || intank > 500) err++ ;
break ;
case 'T':
outtank = getval(optarg, 0) ;
if (outtank < 2 || outtank > 500) err++ ;
break ;
/*
* Uncompress.
*/
case 'u':
action = 'e' ;
break ;
/*
* Version.
*/
case 'v':
#if DEBUG >= 1
(void) fprintf(stderr, "%s (debug %d)\n", VERSION, DEBUG) ;
#else
(void) fprintf(stderr, "%s\n", VERSION) ;
#endif
return(0) ;
/*
* Maximum output bit width.
*/
case 'w':
maxwidth = getval(optarg, 0) ;
if (maxwidth < 17 || maxwidth > 24) err++ ;
break ;
/*
* Read/Write data to/from non-shared memory buffers
* to avoid driver problems.
*/
case 'x':
incopy = 1 ;
break ;
case 'X':
outcopy = 1 ;
break ;
/*
* Command to be performed after last close.
*/
case 'z':
zcommand = optarg ;
break ;
default:
err++ ;
}
}
/*
* Check parameter consistency.
*/
if (insize && !infname)
{
(void) fprintf(stderr, "-n requires -f\n") ;
err++ ;
}
if (outsize && !outfname)
{
(void) fprintf(stderr, "-N requires -F\n") ;
err++ ;
}
/*
* Print the USAGE message.
*/
if (optind != argc || err)
{
(void) fprintf(stderr, "Usage: %s %s%s%s", argv[0],
"[-celqsSvxX] [-a cmd] [-b cbs] [-B ebs] [-f cfile] [-F efile]\n",
"\t[-m memory] [-n cblks] [-N eblks] [-p outpad] [-r runsize]\n",
"\t[-t cbuf] [-T ebuf] [-w width] [-z cmd]\n") ;
exit(2) ;
}
/*
* Adjust compression parameters.
*/
if (action == 'c')
{
long i ;
char *p ;
/*
* Switch parameters around if necessary so the lower
* case options refer to the compressed file, and the
* upper case letters refer to the expanded file.
*/
if (phys == 0) phys = 512 ;
i = insize ; insize = outsize ; outsize = i ;
i = inbs ; inbs = outbs ; outbs = i ;
i = intank ; intank = outtank ; outtank = i ;
i = inswap ; inswap = outswap ; outswap = i ;
i = incopy ; incopy = outcopy ; outcopy = i ;
p = infname ; infname = outfname ; outfname = p ;
}
/*
* Adjust output block size to correspond with
* the requested output padding.
*/
if (phys)
{
if (outbs < phys) outbs = phys ;
else outbs = (outbs + phys - 1) / phys * phys ;
}
/*
* Execute setup command.
*/
if (acommand) (void) system(acommand) ;
/*
* Setup input operation through shared memory.
*/
if (intank)
{
inprocess() ;
getin = shareinput ;
}
else
{
inbuf = (ushort *) malloc((unsigned) inbs+1) ;
getin = fileinput ;
}
/*
* Setup output operation through shared memory.
*/
if (outtank)
{
outprocess() ;
putout = shareoutput ;
}
else
{
outbuf = (ushort *) malloc((unsigned) outbs) ;
putout = fileoutput ;
}
/*
* Lock process into memory.
*/
if (memlock && plock(PROCLOCK) == -1)
{
(void) perror("Cannot lock process in memory") ;
return(2) ;
}
/*
* Setup to gracefully exit.
*/
for (i = 0 ; sig[i] ; i++)
{
if (signal(sig[i], SIG_IGN) != SIG_IGN)
(void) signal(sig[i], quit) ;
}
/*
* Compress or expand input data.
*/
rs = action == 'c' ? compact() : expand() ;
quit(rs) ;
return(0) ;
}